/*****************************************************
	SPI.c Joe Pardue September 18, 2010
	
	>>WORK IN PROGRESS<<
	Soft SPI beta September 10, 2010 tested with CylonEyesSPI on Butterfly
	Soft and Hard tested on Arduino board September 18, 2010

	TODO: Setup and test an ATmega644 version

******************************************************/

// I HATE LICENSES LIKE THIS >>BUT<< I've been told that without
// the license then the work is automatically copyrighted in my name
// since my purpose is to educate (and learn), I want the code to be 
// used by whoever wants to use it to learn something. If you like it, 
// then visit my website www.smileymicros.com and buy something.

/*
 *  BSD License
 *  -----------
 *
 *  Copyright (c) 2008, Smiley Micros, All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 *   
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 *   
 *  - Neither the name of the Smiley Micros nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission. 
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

// And to further cover my butt, let me add that if you use this software
// it will destroy whatever machine you use it on and kill anyone in a one 
// kilometer radius. So don't even consider using it for any reason whatsoever!


/*****************************************************
 	SPI.c provides a library of SPI functions for 
	two types of SPI:

		Software SPI
		Hardware SPI

******************************************************/

#include "SPI.h"

#if defined (SPI0) //|| defined(SPI1)	// RS added

/*****************************************************
	Define SPI0 functions
******************************************************/
void spi0_init_master(void)
{
	#if defined (SPI0_SOFT) //|| defined (SPI1_SOFT)	// RS added
	spi0_soft_init_master();
	#else // HARD
	spi0_hard_init_master();
	#endif
}

uint8_t spi0_master_rw8(uint8_t to_slave)
{
	#if defined (SPI0_SOFT) //|| defined (SPI1_SOFT)	// RS added
	return spi0_soft_master_rw8(to_slave);
	#else // HARD
	return spi0_hard_master_rw8(to_slave);
	#endif
}

uint16_t spi0_master_rw16(uint16_t to_slave)
{
	#if defined (SPI0_SOFT) //|| defined (SPI1_SOFT)	// RS added
	return spi0_soft_master_rw16(to_slave);
	#else // HARD
	return spi0_hard_master_rw16(to_slave);
	#endif
}

#elif defined (SPI1)
/*****************************************************
	Define SPI1 functions
******************************************************/
///* RS added this. Reuses the same functions from SPI0_SOFT
void spi0_init_master(void)
{
	#if defined (SPI1_SOFT)
	spi0_soft_init_master();
	#else // HARD
	spi0_hard_init_master();
	#endif
}

uint8_t spi0_master_rw8(uint8_t to_slave)
{
	#if defined (SPI1_SOFT)
	return spi0_soft_master_rw8(to_slave);
	#else // HARD
	return spi0_hard_master_rw8(to_slave);
	#endif
}

uint16_t spi0_master_rw16(uint16_t to_slave)
{
	#if defined (SPI1_SOFT)
	return spi0_soft_master_rw16(to_slave);
	#else // HARD
	return spi0_hard_master_rw16(to_slave);
	#endif
}


//*/


#elif defined (SPIn)
/*****************************************************
	Define SPIn functions
******************************************************/

#else
   #error "No SPI Number Defined"
#endif


#if defined (SPI0_SOFT) || defined (SPI1_SOFT)
/*****************************************************
	Software SPI
******************************************************/

//void spi_soft_setup_pins()
void spi0_soft_init_master()
{
  	// Initialize MISO as input
  	// set DDR pin register to 0
  	miso_ddr &= ~(1<<miso_port_pin);

  	// Initialize MOSI, SCLK, AND /SS as outputs
  	// set DDR pin registers to 1
    mosi_ddr |= (1<<mosi_port_pin);
	sclk_ddr |= (1<<sclk_port_pin);
  	ss_ddr |= (1<<ss_port_pin); 
}

uint8_t spi0_soft_master_rw8(uint8_t to_slave)
{
	uint8_t from_slave = 0;

	spi_soft_set_ss();

	for(int i=0; i<=7; i++)
	{
		// Send a bit
		if(is_bit_set(to_slave,i))
			spi_soft_set_mosi_bit();
		else
			spi_soft_clear_mosi_bit();

		// Receive a bit
		// temp bits start 0 so change only if 1
		if (miso_port_pins & (1<<miso_port_pin))
			from_slave |= (1 << i);

   		// Toggle the clock
    	spi_soft_toggle_sclk();
	}

	spi_soft_clear_ss();

	return from_slave;

}

// NOTE: this is an educational version with the
// details made explicit. It can be condensed a lot.
uint16_t spi0_soft_master_rw16(uint16_t to_slave)
{
	uint16_t from_slave = 0;

	// Select the slave clear /SS to 0
	spi_soft_clear_ss();

	for(int i=0; i<=15; i++)
	{
		// Send a bit
		if(is_bit_set(to_slave,i))
			spi_soft_set_mosi_bit();
		else
			spi_soft_clear_mosi_bit();

		// Receive a bit
		// temp bits start 0 so change only if 1
		if (miso_port_pins & (1<<miso_port_pin))
			from_slave |= (1 << i);

   		// Toggle the clock
    	spi_soft_toggle_sclk();
	}

	// Deselect the slave set /SS to 1
	spi_soft_set_ss();
	
	return from_slave;
}

// Return the data pin state
uint8_t spi_soft_get_miso_bit()
{
   if (miso_port_pins & (1<<miso_port_pin))
   {
     return 1;
   }
   else return 0;
}

// Toggle the clock
void spi_soft_toggle_sclk()
{
	// Serial bit shifted in on rising clock
   sclk_port |= (1<<sclk_port_pin); 

   // Give some time to react
   _delay_ms(0.2);

   // Set the clock back to 0
   sclk_port &= ~(1<<sclk_port_pin);
}

#elif defined (SPI0_HARD)
/*****************************************************
	Hardware SPI
******************************************************/
void spi0_hard_init_master(void)
{

	//uart_send_string("hardware SPI init");
	//TODO -don't you want to handle this in the header??
	//#if defined (Butterfly)
	// Enable pull-ups TODO VERIFY
	//PORTB |= (1<<PB3) | (1<<PB2) | (1<<PB1) | (1<<PB0);
	PORTB |= (1<< miso_hardware_pin) \
 			| (1<< mosi_hardware_pin) \
 			| (1<< sclk_hardware_pin) \
 			| (1<< ss_hardware_pin);

	//Set MOSI, SCK AND SS to outputs
	DDRB |= (1<< mosi_hardware_ddr) \
			| (1<< sclk_hardware_ddr) \
			| (1<< ss_hardware_ddr);

	// Set Miso to input
	DDRB &= ~(1<<miso_hardware_ddr);

    SPCR = ( 1 << SPE ) | ( 1 << MSTR ) | ( 1 << SPR0 ); 
}

// Simultaneously sends a byte to the slave
// and receives a byte from the slave
uint8_t spi0_hard_master_rw8(uint8_t to_slave)
{
	uint8_t from_slave;

	// select slave
	spi_hard_set_ss();

	// Put byte in SPI data register
	SPDR = to_slave;	

	// Poll SPIF-flag until transfer complete				
	while(!(SPSR & (1<<SPIF)));//0x80));					

	// Get the SPI data reg.
	from_slave = SPDR;	

	// deselect slave
	spi_hard_clear_ss();
							
	//return it
	return from_slave;
							
}

uint16_t spi0_hard_master_rw16(uint16_t to_slave)
{
	uint8_t to_slave_low = (uint8_t) to_slave;
	uint8_t to_slave_high = (uint8_t) (to_slave >> 8);
	uint8_t from_slave_high = 0;
	uint8_t from_slave_low = 0;

	// select slave
	spi_hard_set_ss();

	// Put byte in SPI data register
	SPDR = to_slave_high;	

	// Poll SPIF-flag until transfer complete				
	while(!(SPSR & (1<<SPIF)));

	// Get the SPI data reg.
	from_slave_low = SPDR;

	// Put byte in SPI data register
	SPDR = to_slave_low;	

	// Poll SPIF-flag until transfer complete				
	while(!(SPSR & (1<<SPIF)));

	// Get the SPI data reg.
	from_slave_high = SPDR;
		
	// deselect slave
	spi_hard_clear_ss();

	return ( (uint16_t)(from_slave_high << 8) + (uint16_t)from_slave_low );						
}

#else
   #error "No SPI Type Defined"
#endif
